package hasthi.wsdm;

import static hasthi.wsdm.capabilities.MetricsCapability.FALIED_REQUESTS;
import static hasthi.wsdm.capabilities.MetricsCapability.LAST_REQUEST_RECIVED_TIME;
import static hasthi.wsdm.capabilities.MetricsCapability.LAST_RESPONSE_TIME;
import static hasthi.wsdm.capabilities.MetricsCapability.MAX_RESPONSE_TIME;
import static hasthi.wsdm.capabilities.MetricsCapability.NUMBER_OF_SUCCESSFUL_REQUESTS;
import static hasthi.wsdm.capabilities.MetricsCapability.PENDING_REQUESTS;
import static hasthi.wsdm.capabilities.MetricsCapability.SERVICE_START_TIME;
import hasthi.common.HasthiException;
import hasthi.common.ManagementNotifier;
import hasthi.common.Utils;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.WSDMConstants;
import hasthi.common.constants.WSDMConstants.SystemState;
import hasthi.instrument.JavaInstrumentation;
import hasthi.metamodel.resource.ManagedResource;
import hasthi.metamodel.resource.ManagedResource.ResourceType;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import javax.xml.namespace.QName;

public abstract class ManagedServiceHandle extends AbstractManagedSystemHandle {
    public static final QName SERVICE_MEMEORY_USAGE = new QName(WSDMConstants.EXTREME_MANAGEMENT_NS,"serviceMemeoryUsagePercent");
    public static final QName SERVICE_FILEDESC_USAGE = new QName(WSDMConstants.EXTREME_MANAGEMENT_NS,"serviceFileDescPercent");
    public static final QName SERVICE_THERAD_COUNT = new QName(WSDMConstants.EXTREME_MANAGEMENT_NS,"serviceThreadCount");
    
    
    private AtomicInteger requestReceived = new AtomicInteger(0);

    private AtomicInteger requestSucessful = new AtomicInteger(0);

    private AtomicInteger requestFalied = new AtomicInteger(0);

    private AtomicLong minRequestTime = new AtomicLong(Long.MAX_VALUE);

    private AtomicLong maxRequestTime = new AtomicLong(0);

    // private AtomicLong totRequestTime = new AtomicLong(0);

    private AtomicLong lastRequestProcessingTime = new AtomicLong(0);

    private long lastRequestReceived = -1;

    private Date startTime = new Date();

    protected Map<QName, Object> systemProperties = new HashMap<QName, Object>();

    public ManagedServiceHandle(QName resourceName,QName portTypeName) throws HasthiException {
        this(Utils.getTimestampedName(resourceName), portTypeName,resourceName.getLocalPart(), null);
    }

    public ManagedServiceHandle(QName resourceName, QName portTypeName,String description,
            Map<QName, Object> properties) throws HasthiException {
        super(Utils.getTimestampedName(resourceName), description);
        if (properties != null) {
            this.systemProperties.putAll(properties);
        }
        String resourceCategoery = (String)systemProperties.get(WSDMConstants.RESOURCE_CATEGORY_PARAM);
        if(resourceCategoery == null){
            systemProperties.put(WSDMConstants.RESOURCE_CATEGORY_PARAM, ResourceType.Service.toString());
        }
        systemProperties.put(WSDMConstants.RESOURCE_TYPE_PARAM, portTypeName.toString());
        systemProperties.put(WSDMConstants.HOSTNAME_PARAM, Utils.getLocalHost());
        supportedMatricNames.add(PENDING_REQUESTS);
        supportedMatricNames.add(FALIED_REQUESTS);
        supportedMatricNames.add(MAX_RESPONSE_TIME);
        supportedMatricNames.add(LAST_RESPONSE_TIME);
        supportedMatricNames.add(NUMBER_OF_SUCCESSFUL_REQUESTS);
        supportedMatricNames.add(SERVICE_START_TIME);
        supportedMatricNames.add(LAST_REQUEST_RECIVED_TIME);
        
        supportedMatricNames.add(SERVICE_FILEDESC_USAGE);
        supportedMatricNames.add(SERVICE_MEMEORY_USAGE);
        supportedMatricNames.add(SERVICE_THERAD_COUNT);
    }

 
    public SystemState getCurrentSystemState() {
        int pendingRequests = requestReceived.get() - requestSucessful.get() - requestFalied.get();
        if (pendingRequests > 10) {
            return SystemState.SaturatedState;
        } else if (pendingRequests > 0) {
            return SystemState.BusyState;
        } else {
            return SystemState.IdleState;
        }
    }

    public SystemState[] getLastStateTransition() {
        int pendingRequests = requestReceived.get() - requestSucessful.get() - requestFalied.get();
        if (pendingRequests > 10) {
            return new SystemState[] { SystemState.BusyState, SystemState.SaturatedState };
        } else if (pendingRequests > 0) {
            return new SystemState[] { SystemState.IdleState, SystemState.BusyState };
        } else {
            if (requestSucessful.get() > 0) {
                return new SystemState[] { SystemState.BusyState, SystemState.IdleState };
            } else {
                return new SystemState[] { SystemState.StoppedState, SystemState.IdleState };
            }
        }
    }

    public ManagementNotifier getNotifier() {
        return null;
    }

    public Object getMatric(QName name) throws HasthiException {
        if (name.equals(PENDING_REQUESTS)) {
            int pendingRequests = Math.max(requestReceived.get() - requestSucessful.get()
                    - requestFalied.get(),0);
            return String.valueOf(pendingRequests);
        } else if (name.equals(FALIED_REQUESTS)) {
            return String.valueOf(requestFalied);
        } else if (name.equals(MAX_RESPONSE_TIME)) {
            return String.valueOf(maxRequestTime);
        } else if (name.equals(LAST_RESPONSE_TIME)) {
            return String.valueOf(lastRequestProcessingTime);
        } else if (name.equals(NUMBER_OF_SUCCESSFUL_REQUESTS)) {
            return String.valueOf(requestSucessful);
        } else if (name.equals(SERVICE_START_TIME)) {
            return String.valueOf(startTime.getTime());
        } else if (name.equals(LAST_REQUEST_RECIVED_TIME)) {
            return String.valueOf(lastRequestReceived);
        } else if (name.equals(SERVICE_FILEDESC_USAGE)) {
            return JavaInstrumentation.fileDescrypterUsagePercent();
        } else if (name.equals(SERVICE_MEMEORY_USAGE)) {
            return JavaInstrumentation.getMemoryUsagePercent();
        } else if (name.equals(SERVICE_THERAD_COUNT)) {
            return JavaInstrumentation.getThreadCount();
        } else {
            throw new HasthiException("Unsupported Matric " + name, FaultCode.InvaliedLocalArgumnet);
        }
    }

    public void requestRecived() {
        requestReceived.incrementAndGet();
        lastRequestReceived = System.currentTimeMillis();
    }

    public void requestSucessful(long time) {
        requestSucessful.incrementAndGet();

        long minRequestTimeTmp = minRequestTime.get();
        while (minRequestTimeTmp > time && !minRequestTime.compareAndSet(minRequestTimeTmp, time)) {
            minRequestTimeTmp = minRequestTime.get();
        }

        long maxRequestTimeTmp = maxRequestTime.get();
        while (maxRequestTimeTmp < time && !maxRequestTime.compareAndSet(maxRequestTimeTmp, time)) {
            maxRequestTimeTmp = maxRequestTime.get();
        }

        // if(totRequestTime.get() + time > Long.MAX_VALUE){
        // requestSucessful.set(requestSucessfultmp)
        // }
        // if (requestSucessfultmp > 0) {
        // avgRequestTime = (avgRequestTime * (requestSucessfultmp - 1) + time)
        // / requestSucessfultmp;
        // }
        lastRequestProcessingTime.set(time);
    }

    public void requestFalied() {
        requestFalied.incrementAndGet();
    }

    public void setConfiguration(QName name, Object value) throws HasthiException {
        if (systemProperties.containsKey(name) || supportedConfigurationNames.contains(name)) {
            systemProperties.put(name, value);
        } else {
            throw new HasthiException("Does not support configuration " + name,
                    FaultCode.InvaliedLocalArgumnet);
        }
    }

    public Object getConfiguration(QName name) throws HasthiException {
        return systemProperties.get(name);
    }

}
